package com.rndemo.utils;

import android.graphics.Bitmap;


import com.rndemo.entity.MapJson;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * 这里可以将Object转为 json,map,str...
 *
 *\@JSONType(ignores={"url"})产生json时将不会有url
 */
public class UtilClass {
    private static final String timeStyle="yyyy-MM-dd HH:mm:ss";

    static List<Class> listType = new ArrayList<>();
    static {
        listType.add(List.class);
        listType.add(ArrayList.class);
    }

    static String strType = String.class.getSimpleName();
    static String intType = Integer.TYPE+"";
    static String booleanType = Boolean.TYPE+"";
    static String byteType = Byte.TYPE+"";
    static String charType = char.class.getSimpleName();
    static String shortType = Short.TYPE+"";
    static String floatType = Float.TYPE+"";
    static String longType = Long.TYPE+"";
    static String doubleType = Double.TYPE+"";
    static String dateType = Date.class.getSimpleName();

    /** 表示有modifie中的一个既可*/
    public static final int OR = 0x10000000;
    /** 表示与modifie完全一样  0表示package*/
    public static final int AND = 0x00000000;

    /**
     * 自己写的反谢（对父类也进行反谢）
     */
    public static Map<String, Object> toMap(Object obj){
        return toMap(obj, true);
    }
    /**
     * 由类得到map
     * @param obj
     * @param isF -- 对父类反射吗
     * @return: Map<String,Object>
     */
    public static Map<String, Object> toMap(Object obj,boolean isF){
        return toMap(obj,isF,AND|Modifier.PUBLIC);
    }
    /**
     * 由类得到map
     * @param obj
     * @param isF -- 对父类反射吗
     * @param modifie -- or|Modifier.PUBLIC|...
     * @return: Map<String,Object>
     */
    public static Map<String, Object> toMap(Object obj,boolean isF,int modifie){
        Map<String, Object> map = new MapJson<String,Object>();
        try {
            Class<?> c = obj.getClass();
            do {
                map = toMap(obj, c, map,isF,modifie);
            } while (isF&&(c = c.getSuperclass())!=Object.class);
        } catch (Exception e) {
            Mlog.err(e);
        }
        return map;
    }
    /**
     * @param modifie --反射那些modifie的字断 (注意 package 为0)
     */
    private static Map<String, Object> toMap(Object obj,Class<?> c,Map<String, Object> map,boolean isF,int modifie) throws Exception{
        boolean isOr =(modifie & OR)>0;
        Field[] declaredFields = c.getDeclaredFields();
//		if (obj instanceof List){
//			List list = (List) obj;
//			List<Object> l = new ArrayList<>();
//			for (Object o:list){
//				Map<String, Object> mp = toMap(o,isF,modifie);
//				l.add(mp);
//			}
//			map.put(null, l);
//			return map;
//		}
        for (Field field:declaredFields){
            //修饰符Modifier
            int modifiers = field.getModifiers();
            if (isOr) {//有一个相同即可
                if ((modifie & modifiers)<=0) {
                    continue;
                }
            }else {//完全一样
                if (modifiers!=modifie) {
                    continue;
                }
            }
            // 如int
            String type = field.getType().getSimpleName();
            Class<?> cls = field.getType();
            // 变量名
            String name = field.getName();

            field.setAccessible(true);
            Object object = field.get(obj);
            if (isBaseType(type)) {
                map.put(name, object);
            }else if (listType.contains(cls)){//是list类型
                field.setAccessible(true);
                List list = (List) object;
                List<Object> l = new ArrayList<>();
                for (Object o:list){
                    Map<String, Object> mp = toMap(o,isF,modifie);
                    l.add(mp);
                }
                map.put(name, l);
            }else{//不是基本类型
                if (c.equals(cls) && (Modifier.STATIC & modifiers) == Modifier.STATIC) {//当为单例时
                    continue;
                }
                Map<String, Object> mp = toMap(object,isF,modifie);
                map.put(name, mp);
            }
        }
        return map;
    }

    /**
     * 是否为基本类型
     */
    public static boolean isBaseType(String type){
        if (type.equals(intType)) {
            return true;
        }
        if (type.equals(strType)) {
            return true;
        }
        if (type.equals(booleanType)) {
            return true;
        }
        if (type.equals(byteType)) {
            return true;
        }
        if (type.equals(charType)) {
            return true;
        }
        if (type.equals(shortType)) {
            return true;
        }
        if (type.equals(floatType)) {
            return true;
        }
        if (type.equals(longType)) {
            return true;
        }
        if (type.equals(doubleType)) {
            return true;
        }
        if (type.equals(dateType)) {
            return true;
        }
        return false;
    }

    /**
     * 根据路径反谢
     */
    public static String toUrl(Class<?> c,int index){
        String name = c.getName();
        String[] strs = name.split("\\.");
        String url = "";
        for (int i = index; i < strs.length; i++) {
            url += strs[i] + "/";
        }
        if (isEmpty(url)){
            return url;
        }
        url += '/';
        url = url.replace("//", "");
        return url;
    }
    /**
     * fastJson用得转换
     */
    public static String toJson(Object obj){
//		return  JSON.toJSONString(obj);
        return null;
    }
    /**
     * 如     test=df&test2=test
     */
    public static String toAndStr(Object obj){
        return toSome(obj, '&');
    }
    /**
     * 各字断用c相连
     */
    private static String toSome(Object obj,char c){
        String str = "";
        Map<String, Object> map = toMap(obj);
        Set<Entry<String,Object>> entrySet = map.entrySet();
        for (Entry<String,Object> e:entrySet) {
            str += e.getKey()+"="+e.getValue()+c;
        }
        if (isEmpty(str)){
            return str;
        }
        str += c;
        str = str.replace(""+c+c, "");
        return str;
    }

    /**
     * @param obj 中只能放基本类型和实现了Serializable接口的内容
     * @return
     * @throws Exception
     */
    public static int getSize(Object obj) throws Exception{
        if (obj instanceof Bitmap){
            Bitmap bitmap = (Bitmap) obj;
            return bitmap.getRowBytes() * bitmap.getHeight();
        }
        int size= 0;
        ByteArrayOutputStream baos;
        ObjectOutputStream oos = null;
        try {
            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            size = baos.size();
        } catch (Exception e) {
            throw new Exception(e);
        } finally {
            try {
                oos.close();
            } catch (IOException e) {
            }
        }
        return size;
    }

    private static boolean isEmpty(String str){
        if (str == null || str.length() == 0)
            return true;
        else
            return false;
    }







    //=======================================================

    /**
     * map 得到 类
     *
     * @throws Exception
     * @return: Object
     */
    public  static <T,E> T mapToObj(Map<String, E> map,Class<T> c) {
        T obj = null;
        try {
            obj = c.newInstance();
            return mapToObj(map, obj);
        } catch (Exception e) {
            Mlog.err(e);
        }
        return obj;
    }
    /**
     * map 得到 类
     * @throws Exception
     * @return: Object
     */
    @SuppressWarnings("unchecked")
    public  static <T,E> T mapToObj(Map<String, E> map,final T object) {
        Class<?> c = object.getClass();
        T obj = object;
        Ret<T,E> ret= new Ret<>(obj, map);
        try {
            if (map==null) {
                return null;
            }
            do {
                ret = mapToObj(ret,c);
            } while ((c =  c.getSuperclass())!=null);
        } catch (Exception e) {
            Mlog.err(e);
        }
        return ret.obj;
    }

    private static <T,E> Ret<T,E> mapToObj(Ret<T,E> ret,Class<?> c) throws Exception{
        if (c.equals(Object.class)) {
            return ret;
        }
        Field[] ftemp1 = c.getDeclaredFields();
        Field[] ftemp2 = c.getFields();
        Set<Field> fields = new HashSet<>();
        fields.addAll(Arrays.asList(ftemp1));
        fields.addAll(Arrays.asList(ftemp2));
        Field[] fds = new Field[fields.size()];
        fields.toArray(fds);
        for (Field fd : fds) {
            if (Modifier.isStatic(fd.getModifiers())) {//static 的量
                continue;
            }
            //如int
            String type = fd.getType().getSimpleName();
            //变量名
            String name = fd.getName();
            //map中的真实值
            Object value = ret.map.get(name);
            if (value != null) {
                try {
                    fd.setAccessible(true);
                    if (UtilClass.isBaseType(type)) {
                        value = toObj(fd, ret, type, value);
                    } else {//不是基类
                        Class<?> cls = fd.getType();
                        if (value instanceof MapJson){//这个是为了解析json里的非基本类型
                            value = mapToObj((HashMap)value, cls.newInstance());
                        }else if (value instanceof List){//这个是为了解析json数组
                            Type tc = null;//得到 list里的类型
                            Type t = fd.getGenericType();
                            if (t instanceof ParameterizedType) {
                                tc = ((ParameterizedType) t).getActualTypeArguments()[0];
                            }
                            List list = new ArrayList();
                            for (Object obj:(List) value){
                                if (obj instanceof MapJson && tc!=null){
                                    obj = mapToObj((HashMap)obj, (Class) tc);
                                    list.add(obj);
                                }
                            }
                            value = list;
                        }else{
                            value = mapToObj(ret.map, value);
                        }
                    }
                    fd.set(ret.obj, value);
                } catch (Exception e) {
                    Mlog.err(e);
                }
            }
        }
        return ret;
    }


    private static class Ret<T,E>{
        T obj;
        Map<String, E> map;
        public Ret(T obj, Map<String, E> map) {
            super();
            this.obj = obj;
            this.map = map;
        }
    }

    /**
     * 强转
     * 只能被mapToObj(Ret<T,E> ret,Class c)调用
     * 要与 {@link #toDB(String, Object)} 内容相对应才行
     */
    private static <T,E> Object toObj(Field fd,Ret<T, E> ret,String type,Object value){
        if (type.equals(UtilClass.intType)) {
            return Integer.valueOf(value+"");
        }
        if (type.equals(UtilClass.strType)) {
            return value;
        }
        if (type.equals(UtilClass.booleanType)) {
            return Boolean.valueOf(value+"");
        }
        if (type.equals(UtilClass.byteType)) {
            return Byte.valueOf(value+"");
        }
        if (type.equals(UtilClass.charType)) {
            char[] charArray = value.toString().toCharArray();
            if (charArray.length>0) {
                return charArray[0];
            }
        }
        if (type.equals(UtilClass.shortType)) {
            Short.valueOf(value+"");
        }
        if (type.equals(UtilClass.floatType)) {
            return Float.valueOf(value+"");
        }
        if (type.equals(UtilClass.longType)) {
            return Long.valueOf(value+"");
        }
        if (type.equals(UtilClass.doubleType)) {
            return Double.valueOf(value+"");
        }
        if (type.equals(UtilClass.dateType)) {
            try {
                return UtilDate.getDate(value+"", timeStyle);
            } catch (Exception e) {
                Mlog.err(e);
            }
            return null;
        }
        Class<?> c = fd.getType();

        try {
            return mapToObj(ret.map, c);
        } catch (Exception e) {
            Mlog.err(e);
        }
        return null;
    }
    /**
     * 得到sql想要的内容
     * 要与 {@link #toObj(Field, Ret, String, Object)} 里的内容相对应才行
     */
    static Object toDB(String type,Object obj){
        if (type.equals(UtilClass.strType)) {
            if (obj==null) {
                return "''";//字符串将为""而非null
            }
            return "'"+obj+"'";
        }
        if (type.equals(UtilClass.dateType)) {
            return "'"+UtilDate.getTime((Date)obj, timeStyle)+"'";
        }
        //注意这里的操作应与getObjToObj(String type,String value)内部booleanType的操作相对应
        if (type.equals(UtilClass.booleanType)) {
            return (boolean)obj?"'true'":"'false'";
        }
        return obj;
    }

    /**
     * JSONObject转成map
     * @param jobj
     * @return
     */
    public static Map<String,Object> toMap(JSONObject jobj){
        Map<String,Object> map = new MapJson<String,Object>();
        try {
            Iterator<String> keys = jobj.keys();
            while (keys.hasNext()){
                String key = keys.next();
                try {
                    //
                    JSONArray array = jobj.getJSONArray(key);
                    List<Map<String,Object>> list = new ArrayList<>();
                    for (int i = 0; i < array.length(); i++) {
                        try {
                            JSONObject jobjTemp = array.getJSONObject(i);
                            Map<String, Object> stringObjectMap = toMap(jobjTemp);
                            list.add(stringObjectMap);
                        }catch (Exception e){
                        }
                    }
                    map.put(key,list);
                }catch (Exception e){
                    Object o = jobj.get(key);
                    if (o instanceof JSONObject){
                        map.put(key,toMap((JSONObject)o));
                    }else{
                        map.put(key,o);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }
}
